#pragma once

#include <iostream>

#include <string>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>


using namespace std;

namespace Client
{
    class udpClient
    {
    public:
        udpClient(const string &serverip, const uint16_t &serverport)
            :_sockfd(-1), _serverip(serverip), _serverport(serverport), _quit(false)
        {}
        void initClient()
        {
            // 1.创建socket(只需要创建套接字就行，无须自己显示的绑定port和ip)
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if(_sockfd == -1)
            {
                cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
                exit(1);                
            }
            cout << "client socket success : " << _sockfd << endl;
            // 2.client要不要绑定[必须要的], client要不要显示的绑定, 需不需要程序员自己bind?[不需要]
            // 更重要的是, 写服务器的是一家公司, 写客户端的是无数家公司，万一多个app bind同一个port发生冲突呢？所以只要client有port就行，只要别冲突就好
            // 由OS自动形成端口进行客户端的bind  ---客户端的port是多少根本不重要，只要能保证唯一性就行。 ---OS在什么时候，如何绑定的呢？sendto的时候

        }
        static void *recvMessage(void *args)//demo 3 群聊
        {
            int sockfd = *(static_cast<int*>(args));
            pthread_detach(pthread_self());

            while(true)
            {
                char buffer[1024];
                struct sockaddr_in temp;
                socklen_t temp_len = sizeof(temp);
                size_t n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&temp, &temp_len);
                if(n >= 0) buffer[n] = 0;
                cout << buffer << endl;//输出到1号fd,采用行刷新策略
            }
            return nullptr;
        }
        void run()
        {
            // 多线程方案
            pthread_create(&_reader, nullptr, recvMessage, (void*)&_sockfd);//主线程负责发送消息, 从线程负责接收服务器广播的消息

            struct sockaddr_in server;
            bzero(&server, sizeof(server));
            server.sin_addr.s_addr = inet_addr(_serverip.c_str());//转成网络序列
            server.sin_port = htons(_serverport);
            server.sin_family = AF_INET;

            string message;
            while(!_quit)
            {
                // cout << "Please Enter# ";
                // cout << "[wyn@VM-8-2-centos socket]$ ";
                cerr << "Enter# ";//输出到2号fd,0号和2号fd都是无缓冲的,可以立即将消息刷新到显示器上,1号默认是行缓冲的
                getline(cin, message);

                // 在客户端首次向服务器发送数据的时候,操作系统会给client绑定对应的port和ip 
                sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
                // C++风格的string字符串,不需要发送\0,大小为有效字符个数size()即可


                // char buffer[1024];
                // struct sockaddr_in temp;
                // socklen_t temp_len = sizeof(temp);
                // size_t n = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&temp, &temp_len);
                // if(n >= 0) buffer[n] = 0;
                // cout << "服务器的翻译结果# " << buffer << endl;// demo1 翻译
                // cout << "\n" << buffer << endl;// demo2 执行bash命令

            }
        }
        ~udpClient(){}

    private:
        int _sockfd;
        string _serverip;
        uint16_t _serverport;
        bool _quit;
        pthread_t _reader;
    };
}// namespace Client





















